package cn.lg.soar.mvc.manager;

import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.mvc.model.ClientInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author luguoxiang 469599794@qq.com
 * @Date: 2023/11/23 17:50
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public abstract class AbstractClientInfoManager<T> {
    private static final Logger logger = LoggerFactory.getLogger(AbstractClientInfoManager.class);
    private int size = 0;
    private final Map<Long, List<ClientInfo<T>>> map = new ConcurrentReferenceHashMap<>();

    /**
     * 发送消息
     * @param client
     * @param message
     */
    abstract protected boolean doSendMsg(T client, String message);

    /**
     * 给指定用户发消息
     * @param message
     * @param userIds
     */
    protected Collection<Long> sendMsgByUser(String message, Collection<Long> userIds) {
        if (DataUtil.isEmpty(userIds)) {
            return new ArrayList<>();
        }
        userIds = userIds.stream().distinct().collect(Collectors.toList());
        Set<Long> result = new HashSet<>(userIds.size());
        for (Long userId : userIds) {
            List<ClientInfo<T>> list = map.get(userId);
            if (DataUtil.isEmpty(list)) {
                continue;
            }
            for (ClientInfo<T> clientInfo : list) {
                if (doSendMsg(clientInfo.getClient(), message)) {
                    result.add(userId);
                }
            }
        }
        return result;
    }

    /**
     * 给指定设备发消息
     * @param message 消息
     * @param userIds 用户id
     */
    protected Collection<Long> sendMsgByUser(String message, Long ...userIds) {
        return sendMsgByUser(message, Arrays.asList(userIds));
    }

    /**
     * 群发消息
     * @param message 消息
     */
    protected boolean sendMsgByClient(String message, String clientId) {
        ClientInfo<T> clientInfo = getClientInfo(clientId);
        if (clientInfo == null) {
            return false;
        }
        return doSendMsg(clientInfo.getClient(), message);
    }

    /**
     * 群发消息
     * @param message 消息
     */
    protected Collection<Long> sendMsgAll(String message) {
        Set<Long> result = new HashSet<>(size());
        map.forEach((k, v) -> {
            v.forEach(clientInfo -> {
                if (doSendMsg(clientInfo.getClient(), message)) {
                    result.add(k);
                }
            });
        });
        return result;
    }

    /**
     * 在线客户端数
     * @return
     */
    public int size() {
        return size;
    }

    /**
     * 在线用户数
     * @return
     */
    public int userCount() {
        return map.size();
    }

    /**
     * 加入新连接
     * @param userId
     * @param clientId
     * @param client
     */
    protected void put(Long userId, String clientId, T client) {
        ClientInfo<T> clientInfo = new ClientInfo<>(clientId, client);
        List<ClientInfo<T>> clientInfos = map.computeIfAbsent(userId, k -> new ArrayList<>());
        for (int i = 0, len = clientInfos.size(); i < len; i++) {
            ClientInfo<T> x = clientInfos.get(i);
            if (Objects.equals(client, x.getClient()) || Objects.equals(clientId, x.getClientId())) {
                clientInfos.set(i, clientInfo);
                return;
            }
        }
        clientInfos.add(clientInfo);
        refreshSize();
        logger.info("有新的连接，userId：{}，总数：{}", userId, size());
    }

    /**
     * 删除连接
     * @param userId
     */
    protected List<ClientInfo<T>> remove(Long userId) {
        List<ClientInfo<T>> list = map.remove(userId);
        if (list != null) {
            refreshSize();
            logger.info("有连接关闭，userId：{}，总数：{}", userId, size());
        }
        return list;
    }

    /**
     * 删除连接
     * @param client
     */
    protected ClientInfo<T> remove(T client) {
        Set<Map.Entry<Long, List<ClientInfo<T>>>> entries = map.entrySet();
        for (Map.Entry<Long, List<ClientInfo<T>>> entry : entries) {
            List<ClientInfo<T>> list = entry.getValue();
            for (int i = 0; i < list.size(); i++) {
                ClientInfo<T> clientInfo = list.get(i);
                if (Objects.equals(clientInfo.getClient(), client)) {
                    list.remove(i);
                    refreshSize();
                    if (list.isEmpty()) {
                        remove(entry.getKey());
                    }
                    logger.info("有连接关闭，userId：{}，总数：{}", entry.getKey(), size());
                    return clientInfo;
                }
            }
        }
        return null;
    }

    /**
     * 查找用户id
     * @param client
     * @return
     */
    public Long getUserId(T client) {
        Set<Map.Entry<Long, List<ClientInfo<T>>>> entries = map.entrySet();
        for (Map.Entry<Long, List<ClientInfo<T>>> entry : entries) {
            List<ClientInfo<T>> value = entry.getValue();
            for (ClientInfo<T> clientInfo : value) {
                if (Objects.equals(clientInfo.getClient(), client)) {
                    return entry.getKey();
                }
            }
        }
        return null;
    }

    public ClientInfo<T> getClientInfo(T client) {
        Set<Map.Entry<Long, List<ClientInfo<T>>>> entries = map.entrySet();
        for (Map.Entry<Long, List<ClientInfo<T>>> entry : entries) {
            List<ClientInfo<T>> value = entry.getValue();
            for (ClientInfo<T> clientInfo : value) {
                if (Objects.equals(clientInfo.getClient(), client)) {
                    return clientInfo;
                }
            }
        }
        return null;
    }

    public ClientInfo<T> getClientInfo(String clientId) {
        Set<Map.Entry<Long, List<ClientInfo<T>>>> entries = map.entrySet();
        for (Map.Entry<Long, List<ClientInfo<T>>> entry : entries) {
            List<ClientInfo<T>> value = entry.getValue();
            for (ClientInfo<T> clientInfo : value) {
                if (Objects.equals(clientInfo.getClientId(), clientId)) {
                    return clientInfo;
                }
            }
        }
        return null;
    }

    public String getClientId(T session) {
        ClientInfo<T> clientInfo = getClientInfo(session);
        if (clientInfo == null) {
            return null;
        }
        return clientInfo.getClientId();
    }

    protected void refreshSize() {
        AtomicInteger size = new AtomicInteger();
        map.forEach((k, v) -> {
            size.addAndGet(v.size());
        });
        this.size = size.get();
    }
}
